x86, hvm: Better handling of INVD/WBINVD on VMX and SVM.
authorKeir Fraser <keir@xensource.com>
Fri, 9 Nov 2007 12:05:27 +0000 (12:05 +0000)
committerKeir Fraser <keir@xensource.com>
Fri, 9 Nov 2007 12:05:27 +0000 (12:05 +0000)
Also better handling of MONITOR/MWAIT on VMX.
Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/hvm/svm/emulate.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/svm/vmcb.c
xen/arch/x86/hvm/vmx/vmcs.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/svm/emulate.h
xen/include/asm-x86/hvm/svm/vmcb.h
xen/include/asm-x86/perfc_defn.h

index 615c27b7cbfe59e419f9d17837d4e2717b6d5fbe..a0c3c5cc3b643d13404cdf49c9aec912911aabf8 100644 (file)
@@ -344,6 +344,7 @@ unsigned long svm_rip2pointer(struct vcpu *v)
  * Special case: Last byte, if zero, doesn't need to match. 
  */
 MAKE_INSTR(INVD,   2, 0x0f, 0x08);
+MAKE_INSTR(WBINVD, 2, 0x0f, 0x09);
 MAKE_INSTR(CPUID,  2, 0x0f, 0xa2);
 MAKE_INSTR(RDMSR,  2, 0x0f, 0x32);
 MAKE_INSTR(WRMSR,  2, 0x0f, 0x30);
@@ -378,6 +379,7 @@ MAKE_INSTR(INT3,   1, 0xcc);
 static const u8 *opc_bytes[INSTR_MAX_COUNT] = 
 {
     [INSTR_INVD]   = OPCODE_INVD,
+    [INSTR_WBINVD] = OPCODE_WBINVD,
     [INSTR_CPUID]  = OPCODE_CPUID,
     [INSTR_RDMSR]  = OPCODE_RDMSR,
     [INSTR_WRMSR]  = OPCODE_WRMSR,
index 602f9c6ce8f601fbf653a2475771bb23e94123c8..2e0c98a978a35d131f0d1ae2bb6703257dd63105 100644 (file)
@@ -1942,24 +1942,24 @@ static void svm_vmexit_do_hlt(struct vmcb_struct *vmcb,
     hvm_hlt(regs->eflags);
 }
 
-static void svm_vmexit_do_invd(struct cpu_user_regs *regs)
+static void svm_vmexit_do_invalidate_cache(struct cpu_user_regs *regs)
 {
+    enum instruction_index list[] = { INSTR_INVD, INSTR_WBINVD };
+    struct vcpu *curr = current;
+    struct vmcb_struct *vmcb = curr->arch.hvm_svm.vmcb;
     int inst_len;
-    
-    /* Invalidate the cache - we can't really do that safely - maybe we should 
-     * WBINVD, but I think it's just fine to completely ignore it - we should 
-     * have cache-snooping that solves it anyways. -- Mats P. 
-     */
 
-    /* Tell the user that we did this - just in case someone runs some really 
-     * weird operating system and wants to know why it's not working...
-     */
-    gdprintk(XENLOG_WARNING, "INVD instruction intercepted - ignored\n");
-    
-    inst_len = __get_instruction_length(current, INSTR_INVD, NULL);
+    if ( !list_empty(&(domain_hvm_iommu(curr->domain)->pdev_list)) )
+    {
+        vmcb->general2_intercepts &= ~GENERAL2_INTERCEPT_WBINVD;
+        wbinvd();
+    }
+
+    inst_len = __get_instruction_length_from_list(
+        curr, list, ARRAY_SIZE(list), NULL, NULL);
     __update_guest_eip(regs, inst_len);
-}    
-        
+}
+
 void svm_handle_invlpg(const short invlpga, struct cpu_user_regs *regs)
 {
     struct vcpu *v = current;
@@ -2205,7 +2205,8 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
         break;
 
     case VMEXIT_INVD:
-        svm_vmexit_do_invd(regs);
+    case VMEXIT_WBINVD:
+        svm_vmexit_do_invalidate_cache(regs);
         break;
 
     case VMEXIT_TASK_SWITCH: {
index cfe725c627b706473d03d58dc2c7b8949c3d990c..f7c59b604e9b76266d5730ed19a6c467d5798212 100644 (file)
@@ -127,7 +127,8 @@ static int construct_vmcb(struct vcpu *v)
         GENERAL2_INTERCEPT_VMRUN       | GENERAL2_INTERCEPT_VMMCALL     |
         GENERAL2_INTERCEPT_VMLOAD      | GENERAL2_INTERCEPT_VMSAVE      |
         GENERAL2_INTERCEPT_STGI        | GENERAL2_INTERCEPT_CLGI        |
-        GENERAL2_INTERCEPT_SKINIT      | GENERAL2_INTERCEPT_RDTSCP;
+        GENERAL2_INTERCEPT_SKINIT      | GENERAL2_INTERCEPT_RDTSCP      |
+        GENERAL2_INTERCEPT_WBINVD;
 
     /* Intercept all debug-register writes. */
     vmcb->dr_intercepts = ~0u;
index 304eea48f19a124e199aa51955e1edad0b002ca0..851814dbf59e6f66e0f3c483c7837ab4557cadb1 100644 (file)
@@ -84,6 +84,7 @@ static void vmx_init_vmcs_config(void)
 
     min = (CPU_BASED_HLT_EXITING |
            CPU_BASED_INVLPG_EXITING |
+           CPU_BASED_MONITOR_EXITING |
            CPU_BASED_MWAIT_EXITING |
            CPU_BASED_MOV_DR_EXITING |
            CPU_BASED_ACTIVATE_IO_BITMAP |
index 5e31ba25e51505e4f5d01fb77e98a8acd4182b4b..e4858d1755b71d5c54bbdf17baf2a4b6966eb4ea 100644 (file)
@@ -2881,10 +2881,9 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
         if ( vmx_do_msr_write(regs) )
             __update_guest_eip(inst_len);
         break;
+
     case EXIT_REASON_MWAIT_INSTRUCTION:
     case EXIT_REASON_MONITOR_INSTRUCTION:
-    case EXIT_REASON_PAUSE_INSTRUCTION:
-        goto exit_and_crash;
     case EXIT_REASON_VMCLEAR:
     case EXIT_REASON_VMLAUNCH:
     case EXIT_REASON_VMPTRLD:
@@ -2894,8 +2893,6 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
     case EXIT_REASON_VMWRITE:
     case EXIT_REASON_VMXOFF:
     case EXIT_REASON_VMXON:
-        /* Report invalid opcode exception when a VMX guest tries to execute
-            any of the VMX instructions */
         vmx_inject_hw_exception(v, TRAP_invalid_op, VMX_DELIVER_NO_ERROR_CODE);
         break;
 
@@ -2911,6 +2908,15 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
         break;
     }
 
+    case EXIT_REASON_INVD:
+    {
+        inst_len = __get_instruction_length(); /* Safe: INVD */
+        __update_guest_eip(inst_len);
+        if ( !list_empty(&(domain_hvm_iommu(v->domain)->pdev_list)) )
+            wbinvd();
+        break;
+    }
+
     default:
     exit_and_crash:
         gdprintk(XENLOG_ERR, "Bad vmexit (reason %x)\n", exit_reason);
index c31194c940fa7993bec12b7178d16630a3f4b1bd..dfc66bcaeafbeeff205d18c24cf9c9b91d9abed7 100644 (file)
@@ -43,6 +43,7 @@ typedef enum OPERATING_MODE_ {
 /* Enumerate some standard instructions that we support */
 enum instruction_index {
     INSTR_INVD,
+    INSTR_WBINVD,
     INSTR_CPUID,
     INSTR_RDMSR,
     INSTR_WRMSR,
index 4d08d6a63c10c498997f5f9729a264f508d74211..35e492f36aca0b240080fe1b96037b55f9653646 100644 (file)
@@ -72,7 +72,11 @@ enum GenericIntercept2bits
     GENERAL2_INTERCEPT_CLGI    = 1 << 5,
     GENERAL2_INTERCEPT_SKINIT  = 1 << 6,
     GENERAL2_INTERCEPT_RDTSCP  = 1 << 7,
-    GENERAL2_INTERCEPT_ICEBP   = 1 << 8
+    GENERAL2_INTERCEPT_ICEBP   = 1 << 8,
+    GENERAL2_INTERCEPT_WBINVD  = 1 << 9,
+    GENERAL2_INTERCEPT_MONITOR = 1 << 10,
+    GENERAL2_INTERCEPT_MWAIT   = 1 << 11,
+    GENERAL2_INTERCEPT_MWAIT_CONDITIONAL = 1 << 12
 };
 
 
@@ -291,6 +295,10 @@ enum VMEXIT_EXITCODE
     VMEXIT_SKINIT           = 134,
     VMEXIT_RDTSCP           = 135,
     VMEXIT_ICEBP            = 136,
+    VMEXIT_WBINVD           = 137,
+    VMEXIT_MONITOR          = 138,
+    VMEXIT_MWAIT            = 139,
+    VMEXIT_MWAIT_CONDITIONAL= 140,
     VMEXIT_NPF              = 1024, /* nested paging fault */
     VMEXIT_INVALID          =  -1
 };
index 87d52b59f5e3c25009996722f500695cece810f0..eb18b5fe384fbc820490e6686ed1278915a205bc 100644 (file)
@@ -9,8 +9,8 @@ PERFCOUNTER_ARRAY(exceptions,           "exceptions", 32)
 PERFCOUNTER_ARRAY(vmexits,              "vmexits", VMX_PERF_EXIT_REASON_SIZE)
 PERFCOUNTER_ARRAY(cause_vector,         "cause vector", VMX_PERF_VECTOR_SIZE)
 
-#define VMEXIT_NPF_PERFC 137
-#define SVM_PERF_EXIT_REASON_SIZE (1+137)
+#define VMEXIT_NPF_PERFC 141
+#define SVM_PERF_EXIT_REASON_SIZE (1+141)
 PERFCOUNTER_ARRAY(svmexits,             "SVMexits", SVM_PERF_EXIT_REASON_SIZE)
 
 PERFCOUNTER(seg_fixups,             "segmentation fixups")